package com.example.travelcomponentdemo;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.Circle;
import com.google.android.gms.maps.model.CircleOptions;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.widget.RadioGroup;
import android.widget.Toast;

public class MapCircleLineIntersect extends FragmentActivity 
						implements GoogleMap.OnMapClickListener{
	
	private RadioGroup rd_group;
	
	/** google map */
	private GoogleMap mGoogleMap; 
	private static final int MAP_ZOOM_SIZE = 14;
	private static final int CIRCLE_RADIUS = 200;
	
	private int CIRCLE_TYPE = 1;
	private int LINE_TYPE = 2;
	private int TYPE_DRAW = CIRCLE_TYPE;
	private Circle circle;
	private Marker marker_circle;
	
	private Polyline line;
	private Marker marker_line1;
	private Marker marker_line2;
	private int line_marker_count = 0;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.map_with_line);
		
		rd_group = (RadioGroup) findViewById(R.id.radioGroup1);
		rd_group.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
			
			@Override
			public void onCheckedChanged(RadioGroup group, int checkedId) {
				switch (checkedId) {
				case R.id.rd_add_circle:
					TYPE_DRAW = CIRCLE_TYPE;
					break;
				case R.id.rd_add_line:
					TYPE_DRAW = LINE_TYPE;
					break;
				default:
					break;
				}
			}
		});
		
		SupportMapFragment fragment = (SupportMapFragment) getSupportFragmentManager()
				.findFragmentById(R.id.fragment_map);
		mGoogleMap = fragment.getMap();
		mGoogleMap.setOnMapClickListener(this);
	}
	
	private List<LatLng> getCircleLineIntersectionPoint(LatLng pointA,
			LatLng pointB, LatLng center, double radius) {
        double baX = pointB.latitude - pointA.latitude;
        double baY = pointB.longitude - pointA.longitude;
        double caX = center.latitude - pointA.latitude;
        double caY = center.longitude - pointA.longitude;

        double a = baX * baX + baY * baY;
        double bBy2 = baX * caX + baY * caY;
        double c = caX * caX + caY * caY - (radius/100000) * (radius/100000);

        double pBy2 = bBy2 / a;
        double q = c / a;

        double disc = pBy2 * pBy2 - q;
        if (disc < 0) {
            return Collections.emptyList();
        }
        // if disc == 0 ... dealt with later
        double tmpSqrt = Math.sqrt(disc);
        double abScalingFactor1 = -pBy2 + tmpSqrt;
        double abScalingFactor2 = -pBy2 - tmpSqrt;

        double p1X = pointA.latitude - baX * abScalingFactor1;
        double p1Y = pointA.longitude - baY * abScalingFactor1;
        
        LatLng p1 = new LatLng(p1X, p1Y);
        if (disc == 0) { // abScalingFactor1 == abScalingFactor2
            return Collections.singletonList(p1);
        }
        LatLng p2 = new LatLng((pointA.latitude - baX * abScalingFactor2), 
        					(pointA.longitude - baY * abScalingFactor2));
        
        boolean checkPoint1 = checkPointInSegment(pointA, pointB, p1);
        boolean checkPoint2 = checkPointInSegment(pointA, pointB, p2);
        
        if(checkPoint1 && checkPoint2)
        	return Arrays.asList(p1, p2);
        return Collections.singletonList(p1);
    }
	
	public boolean getCircleLineIntersection(LatLng pointA,
			LatLng pointB, LatLng center, double radius){
		double dLat = pointB.latitude - pointA.latitude;
		double dLon = pointB.longitude - pointA.longitude;
		
		double a = (pointB.latitude - pointA.latitude) * center.latitude;
		double b = (pointB.longitude - pointA.longitude) * center.longitude;
		double c = (pointA.latitude - pointB.latitude) * pointA.longitude;
		double d = (pointA.longitude - pointB.longitude) * pointA.latitude;
		
		double sum = Math.abs(a + b + c + d + d);
		double dsum = Math.sqrt(dLat * dLat + dLon * dLon);
		
		if((sum/dsum) < radius) 
			return true;
		return false;
		
	}
	
	private boolean checkPointInSegment(LatLng pointA, LatLng pointB, LatLng pointC){
		double left, top, right, bottom;
		if(pointA.latitude < pointB.latitude) {
			left = pointA.latitude;
			right = pointB.latitude;
		} else if(pointA.latitude > pointB.latitude){
			left = pointB.latitude;
			right = pointA.latitude;
		} else {
			left = 0;
			right = 0;
		}
		
		if(pointA.longitude < pointB.longitude) {
			top = pointA.longitude;
			bottom = pointB.longitude;
		} else if(pointA.longitude > pointB.longitude) {
			top = pointB.longitude;
			bottom = pointA.longitude;
		} else {
			top = 0;
			bottom = 0;
		}
		
		if(left == 0 && right == 0) {
			if(pointC.longitude > top && pointC.longitude < bottom)
				return true;
		}
		
		if(top == 0 && bottom == 0) {
			if(pointC.latitude > left && pointC.latitude < right)
				return true;
		}
		
		if((pointC.longitude > top && pointC.longitude < bottom) 
				&& (pointC.latitude > left && pointC.latitude < right))
			return true;
		
		return false;
//		double dx = pointA.latitude - pointB.longitude;
//		double dy = pointA.longitude - pointB.longitude;
//		
//		double slope = dx / dy;
//		double intercept = pointA.longitude - slope * pointA.latitude;
//		Log.e("checkSegment", "check point: " + Math.abs(pointC.longitude - (slope * pointC.latitude + intercept)));
//		if(Math.abs(pointC.longitude - (slope * pointC.latitude + intercept)) < 0.001f){
//			return true;
//		}
//			
//		return false;
	}

	@Override
	public void onMapClick(LatLng point) {
		Log.e("Intersection", "point: " + point.latitude + "; " 
									+ point.longitude);
		if(TYPE_DRAW == CIRCLE_TYPE) {
			// remove marker and circle
			if(marker_circle != null)
				marker_circle.remove();
			if(circle != null)
				circle.remove();
			
			// add new
			Bitmap bm1 = BitmapFactory.decodeResource(getResources(), R.drawable.menu_icon_07_01);
	        Bitmap bm2 = BitmapFactory.decodeResource(getResources(), R.drawable.menu_icon_08_01);
			marker_circle = mGoogleMap.addMarker(new MarkerOptions().position(point)
					.icon(BitmapDescriptorFactory.fromBitmap(combineImages(bm1, bm2))));
			circle = mGoogleMap.addCircle(new CircleOptions().center(point)
					.radius(CIRCLE_RADIUS)
					.strokeColor(Color.parseColor("#FF0000"))
					.strokeWidth(1)
					.fillColor(Color.parseColor("#11FF0000")));
		} else {
			if(line_marker_count == 0){
				marker_line1 = mGoogleMap.addMarker(new MarkerOptions().position(point));
				line_marker_count ++;
			} else if(line_marker_count == 1) {
				marker_line2 = mGoogleMap.addMarker(new MarkerOptions().position(point));
				marker_line2.setDraggable(true);
				
				line = mGoogleMap.addPolyline(new PolylineOptions()
								.add(marker_line1.getPosition(), 
									marker_line2.getPosition())
								.width(1).color(Color.RED));
				
				line_marker_count ++;
				
				if(marker_circle != null && marker_line1 != null
						&& marker_line2 != null){
//					List<LatLng> intersectPoint = getCircleLineIntersectionPoint(marker_line1.getPosition(), 
//							marker_line2.getPosition(), marker_circle.getPosition(), CIRCLE_RADIUS);
					
//					if(intersectPoint.size() > 0) {
//						Log.e("Intersection", "point1: " + intersectPoint.get(0).latitude + "; " 
//															+ intersectPoint.get(0).longitude);
//						mGoogleMap.addMarker(new MarkerOptions().position(intersectPoint.get(0)));
//						CameraUpdate camera = CameraUpdateFactory
//								.newCameraPosition(new CameraPosition.Builder()
//									.target(intersectPoint.get(0)).zoom(MAP_ZOOM_SIZE).build());
//						mGoogleMap.animateCamera(camera);
//					}
//					if(intersectPoint.size() > 1) {
//						Log.e("Intersection", "point2: " + intersectPoint.get(1).latitude + "; " 
//															+ intersectPoint.get(1).longitude);
//						mGoogleMap.addMarker(new MarkerOptions().position(intersectPoint.get(1)));
//					}
					
//					Toast.makeText(this, "intersect:" + intersectPoint.size(), Toast.LENGTH_SHORT).show();
					
					if(getCircleLineIntersection(marker_line1.getPosition(), 
							marker_line2.getPosition(), marker_circle.getPosition(), CIRCLE_RADIUS)) {
						Toast.makeText(this, "intersect", Toast.LENGTH_SHORT).show();
					} else 
						Toast.makeText(this, "no intersect", Toast.LENGTH_SHORT).show();
				}
			} else if(line_marker_count == 2) {
				marker_line1.remove();
				marker_line2.remove();
				line.remove();
				
				marker_line1 = mGoogleMap.addMarker(new MarkerOptions().position(point));
				line_marker_count = 1;
			}
		}
	}

	public Bitmap combineImages(Bitmap c, Bitmap s) { // can add a 3rd parameter 'String loc' if you want to save the new image - left some code to do that at the bottom 
	    Bitmap cs = null; 

	    int width, height = 0; 

	    if(c.getWidth() > s.getWidth()) { 
	      width = c.getWidth() + s.getWidth(); 
	      height = c.getHeight(); 
	    } else { 
	      width = s.getWidth() + s.getWidth(); 
	      height = c.getHeight(); 
	    } 

	    cs = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 

	    Canvas comboImage = new Canvas(cs); 

	    comboImage.drawBitmap(c, 0f, 0f, null); 
	    comboImage.drawBitmap(s, c.getWidth() - 20, 0f, null); 

	    // this is an extra bit I added, just incase you want to save the new image somewhere and then return the location 
	    /*String tmpImg = String.valueOf(System.currentTimeMillis()) + ".png"; 

	    OutputStream os = null; 
	    try { 
	      os = new FileOutputStream(loc + tmpImg); 
	      cs.compress(CompressFormat.PNG, 100, os); 
	    } catch(IOException e) { 
	      Log.e("combineImages", "problem combining images", e); 
	    }*/ 

	    return cs; 
	  } 
}
